<!-- 单图上传组件 -->
<template>
  <el-upload
    v-model="modelValue"
    class="single-upload"
    list-type="picture-card"
    :show-file-list="false"
    :accept="props.accept"
    :before-upload="handleBeforeUpload"
    :http-request="handleUpload"
  >
    <template #default>
      <el-image v-if="modelValue" :src="modelValue" />
      <el-icon v-if="modelValue" class="single-upload__delete-btn" @click.stop="handleDelete">
        <CircleCloseFilled />
      </el-icon>
      <el-icon v-else class="single-upload__add-btn">
        <Plus />
      </el-icon>
      <el-icon v-if="modelValue" class="single-upload__preview-btn" @click.stop="handlePreview(modelValue)">
        <View />
      </el-icon>
    </template>
  </el-upload>

  <el-image-viewer
    v-if="showPreview"
    :url-list="srcList"
    show-progress
    :initial-index=previewImageIndex
    @close="showPreview = false"
  />

</template>

<script setup lang="ts">
import { UploadRawFile, UploadRequestOptions, ElMessage } from "element-plus";
import ConfigAPI from '@/api/system/config';

const props = defineProps({
  /**
   * 请求携带的额外参数
   */
  data: {
    type: Object,
    default: () => {
      return {};
    },
  },
  /**
   * 上传文件的参数名
   */
  name: {
    type: String,
    default: "file",
  },
  /**
   * 最大文件大小（单位：M）
   */
  maxFileSize: {
    type: Number,
    default: 10,
  },

  /**
   * 上传图片格式，默认支持所有图片(image/*)，指定格式示例：'.png,.jpg,.jpeg,.gif,.bmp'
   */
  accept: {
    type: String,
    default: "image/*",
  },

  /**
   * 自定义样式，用于设置组件的宽度和高度等其他样式
   */
  style: {
    type: Object,
    default: () => {
      return {
        width: "150px",
        height: "150px",
      };
    },
  },
});

const modelValue = defineModel("modelValue", {
  type: String,
  default: () => "",
});

/**
 * 定义组件触发的事件
 */
const emit = defineEmits<{
  (e: 'success', fileInfo: UploadFilePath): void;
  (e: 'error', error: any): void;
  (e: 'input', value: string): void;
  (e: 'onSuccess', fileInfo: UploadFilePath): void;
  (e: 'onError', error: any): void;
}>();

/**
 * 限制用户上传文件的格式和大小
 */
function handleBeforeUpload(file: UploadRawFile) {
  // 校验文件类型：虽然 accept 属性限制了用户在文件选择器中可选的文件类型，但仍需在上传时再次校验文件实际类型，确保符合 accept 的规则
  const acceptTypes = props.accept.split(",").map((type) => type.trim());

  // 检查文件格式是否符合 accept
  const isValidType = acceptTypes.some((type) => {
    if (type === "image/*") {
      // 如果是 image/*，检查 MIME 类型是否以 "image/" 开头
      return file.type.startsWith("image/");
    } else if (type.startsWith(".")) {
      // 如果是扩展名 (.png, .jpg)，检查文件名是否以指定扩展名结尾
      return file.name.toLowerCase().endsWith(type);
    } else {
      // 如果是具体的 MIME 类型 (image/png, image/jpeg)，检查是否完全匹配
      return file.type === type;
    }
  });

  if (!isValidType) {
    ElMessage.warning(`上传文件的格式不正确，仅支持：${props.accept}`);
    return false;
  }

  // 限制文件大小
  if (file.size > props.maxFileSize * 1024 * 1024) {
    ElMessage.warning("上传图片不能大于" + props.maxFileSize + "M");
    return false;
  }
  return true;
}

/*
 * 上传图片
 */
async function handleUpload(options: UploadRequestOptions) {
  try {
    const file = options.file;
    const formData = new FormData();
    
    formData.append(props.name, file);

    // 处理附加参数
    for (const [key, value] of Object.entries(props.data)) {
      formData.append(key, String(value));
    }

    const response = await ConfigAPI.uploadFile(formData);

    if (response.data.code === 0 && response.data) {
      const fileInfo: UploadFilePath = response.data.data;
      // 调用成功回调
      onSuccess(fileInfo);
      return fileInfo;
    } else {
      const errorMsg = response.data.msg || '上传失败';
      ElMessage.error(errorMsg);
      throw new Error(errorMsg);
    }
  } catch (error) {
    onError(error instanceof Error ? error : new Error(String(error)));
    throw error;
  }
}

/**
 * 删除图片
 */
function handleDelete() {
  modelValue.value = "";
}

/**
 * 预览
 */
const showPreview = ref(false); // 是否显示预览
const srcList = ref<string[]>([]);
const previewImageIndex = ref(0); // 预览图片的索引

function handlePreview(imagePath: string) {
  srcList.value = [imagePath];
  previewImageIndex.value = 0;
  showPreview.value = true;
}

/**
 * 上传成功回调
 *
 * @param fileInfo 上传成功后的文件信息
 */
const onSuccess = (fileInfo: UploadFilePath) => {
  modelValue.value = fileInfo.file_url; // 更新绑定的值为文件URL
  emit('onSuccess', fileInfo); // 触发 onSuccess 事件
  emit('success', fileInfo); // 触发 success 事件
  emit('input', fileInfo.file_url); // 触发 input 事件，通知父组件值变化
};

/**
 * 上传失败回调
 */
const onError = (error: any) => {
  console.log("onError", error);
  emit('onError', error); // 触发 onError 事件
  emit('error', error); // 触发 error 事件
};

</script>

<style scoped lang="scss">
:deep(.el-upload--picture-card) {
  width: v-bind("props.style.width");
  height: v-bind("props.style.height");
}

.single-upload {
  position: relative;
  width: v-bind("props.style.width");
  height: v-bind("props.style.height");
  overflow: hidden;
  cursor: pointer;
  border: 1px var(--el-border-color) solid;
  border-radius: 5px;

  &:hover {
    border-color: var(--el-color-primary);
  }

  &__delete-btn {
    position: absolute;
    top: 1px;
    right: 1px;
    font-size: 16px;
    color: #ff7901;
    cursor: pointer;
    background: #fff;
    border-radius: 100%;

    :hover {
      color: #ff4500;
    }
  }
  
  &__preview-btn {
    position: absolute;
    top: 1px;
    left: 1px;
    font-size: 16px;
    color: #409eff;
    cursor: pointer;
    background: #fff;
    border-radius: 100%;

    :hover {
      color: #1d7bff;
    }
  }
}
</style>
